package com.hmdp.controller;


import com.hmdp.dto.Result;
import com.hmdp.enums.ErrorCodeEnum;
import com.hmdp.service.IVoucherOrderService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * 优惠券秒杀抢购前端控制器
 *
 * @author leizi
 */
@RestController
@RequestMapping("/voucher-order")
@Slf4j
public class VoucherOrderController {

    @Resource
    private IVoucherOrderService voucherOrderService;

    @Resource
    private RedissonClient redissonClient;

    /**
     * 秒杀抢购优惠券
     *
     * @param voucherId 秒杀优惠券id
     * @return
     */
    @PostMapping("seckill/{id}")
    public Result seckillVoucher(@PathVariable("id") Long voucherId) {

        /*
         * 使用分布式锁解决用户重复抢购问题（一个人只能抢购一张优惠券）
         *
         * 锁要加载方法外面，因为方法上使用事务，如果锁在service方法内
         * 由于多个线程同时进入方法，都有各自的事务，在判断人员是否已经
         * 有订单时，由于事务间隔离性导致已经更新了订单表但是后面获取到锁
         * 的线程内事务是看不到的，会产生一人生成多个订单问题
         * */
        RLock rLock = redissonClient.getLock("lock:order:" + voucherId);
        try {
            boolean lock = rLock.tryLock(3, 5, TimeUnit.SECONDS);
            if (!lock) {
                return Result.fail(ErrorCodeEnum.VOUCHER_SOLD_RETRY);
            }
            return voucherOrderService.seckillVoucher(voucherId);
        } catch (Exception e) {
            log.error("secondsKill voucher error:{}", e);
            return Result.fail(ErrorCodeEnum.VOUCHER_SOLD_RETRY);
        } finally {
            rLock.unlock();
        }
    }
}
